package chh3933.generator;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.FileType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

import java.io.File;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * mybatis plus 的代码生成功能，附带全套自定义模板
 *
 * @author DingDong
 * @date 2022-08-27
 */
public class MyGenerator {

    // 生成的实体类忽略表前缀: 不需要则置空
    private static String ENTITY_IGNORE_PREFIX = "";
    // 表名数组
//    private static String[] TABLES ="sys_user"
//            .split(",");
    // 数据库
    private static String DB_URL = MyGeneratorConfig.JdbcUrl;
    private static DbType DB_TYPE = DbType.MYSQL;
    private static String DB_DRIVER = "com.mysql.cj.jdbc.Driver";
    private static String DB_SCHEMA = MyGeneratorConfig.DbSchemalName;//"oa";
    private static String DB_USER_NAME = MyGeneratorConfig.DbName;//"root";
    private static String DB_PASSWORD = MyGeneratorConfig.DbPWD;//"root";
    // 输出路径
    private static String OUTPUT_PATH = null;
    private static String OUTPUT_PATH_XML = null;//
    private static String OUTPUT_PATH_POJO=null;
    // 父包名
    private static String PARENT_PACKAGE = MyGeneratorConfig.PackagePath;//"cn3933.dingdong.admin";
    // 下边是分类包名
    // 输出包名 = 父包名 + "." + 分类包名
    private static String CONTROLLER_PACKAGE = "controller";
    private static String SERVICE_PACKAGE = "service";
    private static String SERVICE_IMPL_PACKAGE = "service.impl";
    private static String MAPPER_PACKAGE = "mapper";
    private static String ENTITY_PACKAGE = "entity";
    // mapper存放路径 = OUTPUT_PATH_XML + MAPPER_XML_PACKAGE
    private static String MAPPER_XML_PACKAGE = "mapper";
    // 模板路径
    private static JSONObject templates = new JSONObject();

    // 注释掉模板表示不生成该类模板
    static {
        templates.put("CONTROLLER_TEMPLATE", "templates/vm/controller.java.vm");
        templates.put("SERVICE_TEMPLATE", "templates/vm/service.java.vm");
        templates.put("SERVICE_IMPL_TEMPLATE", "templates/vm/serviceImpl.java.vm");
        templates.put("ENTITY_TEMPLATE", "templates/vm/entity.java.vm");
        templates.put("ENTITY_TEMPLATE_BEAN", "templates/vm/entityBean.java.vm");
        templates.put("MAPPER_TEMPLATE", "templates/vm/mapper.java.vm");
        templates.put("MAPPER_XML_TEMPLATE", "templates/vm/mapper.xml.vm");
        templates.put("VUE_LIST", "templates/vm/list.vue.vm");
        templates.put("VUE_LIST_JS", "templates/vm/list.vue.js.vm");
        templates.put("VUE_LIST_EDIT", "templates/vm/listedit.vue.vm");
//      templates.put("HTML_EDIT", "templates/vm/edit.html.vm");
//      templates.put("C_SHARP_ENTITY_TEMPLATE", "templates/C#entity.cs.ftl");
        if(MyGeneratorConfig.ProjectPath==null)
            MyGeneratorConfig.ProjectPath = System.getProperty("user.dir");
        OUTPUT_PATH = MyGeneratorConfig.ProjectPath + "/src/main/java/";//设置代码生成路径
        OUTPUT_PATH_XML = MyGeneratorConfig.ProjectPath + "/src/main/java/";//设置代码生成路径
        OUTPUT_PATH_POJO = MyGeneratorConfig.PojoPath + "/src/main/java/";//映射对象路径
    }

    // 生成的实体类尾缀  例如: UserEntity
    private static String Entity_SUFFIX = "";

    public static void main(String args[]) {
        // 全局配置
        GlobalConfig globalConfig = globalConfig();
        // 数据库连接配置
        DataSourceConfig dataSourceConfig = dataSourceConfig();
        // 策略配置
        StrategyConfig strategyConfig = strategyConfig();
        // 包配置
        PackageConfig packageConfig = packageConfig();
        // 模板配置
        TemplateConfig templateConfig = templateConfig();
        // 自定义配置
        InjectionConfig injectionConfig = injectionConfig();
        //权限目录start
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入当前菜单名称:");
        String parentName = null, nowName = scanner.next();;
        while(true){
            try{
                System.out.println("请输入上级菜单名称:(0表示根目录)");
                parentName = scanner.next();
                if(DoSqlExe.isExist(parentName)){
                    System.out.println("父菜单正常！");
                    break;
                }else{
                    System.out.println("数据不存在！请重新输入！");
                }
            }catch (Exception e){
                e.printStackTrace();
                System.out.println("上级菜单名称输入错误，终止运行！");
            }
        }
        //权限目录end
        // 执行
        AutoGenerator autoGenerator = new AutoGenerator();
        autoGenerator.setGlobalConfig(globalConfig)
                .setDataSource(dataSourceConfig)
                .setStrategy(strategyConfig)
                .setPackageInfo(packageConfig)
                // 因为使用了自定义模板,所以需要把各项置空否则会多生成一次
                .setTemplate(templateConfig)
                // 使用的模板引擎，如果不是默认模板引擎则需要添加模板依赖到pom,现在用的是freemarker，需要导包
                //.setTemplateEngine(new FreemarkerTemplateEngine())
                .setCfg(injectionConfig)
                .execute();

        // 用于查看配置中都有什么，方便写ftl模板时候自己去找配置，写到ftl中
        //System.out.println("================= 配置信息 =======================");
        //System.out.println(JSON.toJSONString(autoGenerator.getConfig(), SerializerFeature.PrettyFormat));

        List<TableInfo> list = autoGenerator.getConfig().getTableInfoList();
        for (TableInfo t:list) {
            System.out.println("================= 表格信息 =======================");
            System.out.println(JSON.toJSONString(t,SerializerFeature.PrettyFormat));
            System.out.println("================= 创建权限信息 =======================");
            try {
                DoSqlExe.doCreatePer(parentName,nowName,t.getEntityPath());
            } catch (SQLException throwables) {
                throwables.printStackTrace();
                System.out.println("================= 创建权限失败  ！！！！！！！！！");
            }
        }
    }

    /**
     * 全局配置
     */
    private static GlobalConfig globalConfig() {
        return new GlobalConfig()
                // 打开文件
                .setOpen(false)
                // 文件覆盖
                .setFileOverride(true)
                // 开启activeRecord模式，开启之后就可以直接使用entity对数据库进行操作
                .setActiveRecord(false)
                // XML ResultMap: mapper.xml生成查询映射结果
                .setBaseResultMap(true)
                // XML ColumnList: mapper.xml生成查询结果列
                .setBaseColumnList(true)
                // swagger注解; 须添加swagger依赖
                .setSwagger2(MyGeneratorConfig.SetSwagger2)
                // 作者
                .setAuthor(MyGeneratorConfig.AUTHOR)
                // 设置实体类后缀名称
                .setEntityName("%s" + Entity_SUFFIX);
    }

    /**
     * 数据源配置
     */
    private static DataSourceConfig dataSourceConfig() {
        // 数据源配置
        return new DataSourceConfig()
                // 地址
                .setUrl(DB_URL)
                // 数据库名
                .setSchemaName(DB_SCHEMA)
                // 驱动
                .setDriverName(DB_DRIVER)
                // 用户名
                .setUsername(DB_USER_NAME)
                // 密码
                .setPassword(DB_PASSWORD)
                // 数据库类型
                .setDbType(DB_TYPE);
    }

    /**
     * 策略配置
     */
    private static StrategyConfig strategyConfig() {
        StrategyConfig strategyConfig= new StrategyConfig()
                // 表名生成策略：下划线连转驼峰
                .setNaming(NamingStrategy.underline_to_camel)
                // 表字段生成策略：下划线连转驼峰
                .setColumnNaming(NamingStrategy.underline_to_camel)
                // 需要生成的表
                .setInclude(scanner("表名，多个英文逗号分割").split(","))
                // 生成controller
                .setRestControllerStyle(true)
                // 去除表前缀
                .setTablePrefix(ENTITY_IGNORE_PREFIX)
                // controller映射地址：驼峰转连字符
                .setControllerMappingHyphenStyle(false)
                // 是否为lombok模型; 需要lombok依赖
                .setEntityLombokModel(true)
                //逻辑删除字段
                .setLogicDeleteFieldName("deleted")
                //乐观锁字段
                .setVersionFieldName("version")
                // 生成实体类字段注解
                .setEntityTableFieldAnnotationEnable(true);
        strategyConfig.setSuperControllerClass("BaseController");
        return strategyConfig;
    }

    /**
     * 包配置
     * 设置包路径用于导包时使用，路径示例：com.path
     */
    private static PackageConfig packageConfig() {
        return new PackageConfig()
                // 如果下面包没写全，还有前缀那就加上，如果包写全了就不加了
                .setParent(PARENT_PACKAGE)
                .setController(CONTROLLER_PACKAGE)
                .setService(SERVICE_PACKAGE)
                .setServiceImpl(SERVICE_IMPL_PACKAGE)
                .setEntity(ENTITY_PACKAGE)
                .setMapper(MAPPER_PACKAGE)
                .setXml(MAPPER_PACKAGE);
    }

    /**
     * 模板配置
     */
    private static TemplateConfig templateConfig() {
        return new TemplateConfig()
                // 置空后方便使用自定义输出位置
                .setEntity(null)
                .setXml(null)
                .setMapper(null)
                .setService(null)
                .setServiceImpl(null)
                .setController(null);
    }

    /**
     * 自定义配置
     */
    private static InjectionConfig injectionConfig() {
        // 自定义日期和日期格式
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"),formatID = new SimpleDateFormat("yyyyMMddHHmmss");
        String dateString = format.format(date), fid = formatID.format(date)+( (int)(Math.random()*100) );
        Map<String, Object> userDefiedData = new HashMap<>();
        userDefiedData.put("createTime", dateString);
        userDefiedData.put("schema", DB_SCHEMA);
        userDefiedData.put("BeanPackagePath",MyGeneratorConfig.PojoPackagePath);
        userDefiedData.put("ChhModuleName",MyGeneratorConfig.ModuleName);
        userDefiedData.put("ChhModuleNameUP",MyGeneratorConfig.ModuleName.toUpperCase(Locale.ROOT));
        userDefiedData.put("SerialVersionUIDEntity",fid);
        //userDefiedData.put("QuanXianIndex",MyGeneratorConfig.ModuleName+":"+entityPath);
        return new InjectionConfig() {
            @Override
            public void initMap() {
                // 注入配置
            }
        }
                .setFileCreate(new IFileCreate() {// 判断是否创建文件
                    @Override
                    public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                        // 检查文件目录，不存在自动递归创建
                        createFile(filePath);
                        // 指定需要覆盖的文件, 文件不存在就创建，否则只覆盖Dao,Mapper.xml,Mapper.java,Model.cs
                        if (isExists(filePath)) {
                            if (filePath.endsWith("Mapper.xml") || filePath.endsWith("Mapper.java") || filePath.endsWith("Model.cs")) {
                                return true;
                            }
                            return false;
                        }
                        return true;
                    }
                })
                .setFileOutConfigList(fileOutConfigList())// 自定义输出文件
                .setMap(userDefiedData);// 自定义参数使用cfg.xxx来调用
    }

    /**
     * 自定义输出文件配置
     */
    private static List<FileOutConfig> fileOutConfigList() {
        List<FileOutConfig> list = new ArrayList<>();
        String CONTROLLER_TEMPLATE = templates.getString("CONTROLLER_TEMPLATE");
        String SERVICE_TEMPLATE = templates.getString("SERVICE_TEMPLATE");
        String SERVICE_IMPL_TEMPLATE = templates.getString("SERVICE_IMPL_TEMPLATE");
        String ENTITY_TEMPLATE = templates.getString("ENTITY_TEMPLATE");
        String ENTITY_TEMPLATE_BEAN = templates.getString("ENTITY_TEMPLATE_BEAN");
        String MAPPER_TEMPLATE = templates.getString("MAPPER_TEMPLATE");
        String MAPPER_XML_TEMPLATE = templates.getString("MAPPER_XML_TEMPLATE");

        String VUE_LIST = templates.getString("VUE_LIST");
        String VUE_LIST_JS = templates.getString("VUE_LIST_JS");
        String VUE_LIST_EDIT = templates.getString("VUE_LIST_EDIT");
        // 实体类文件输出
        list.add(new FileOutConfig(ENTITY_TEMPLATE) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return OUTPUT_PATH + replaceDot(PARENT_PACKAGE, ENTITY_PACKAGE) + "/" + tableInfo.getEntityName() + StringPool.DOT_JAVA;
            }
        });

        // 实体类Bean文件输出 践行
        list.add(new FileOutConfig(ENTITY_TEMPLATE_BEAN) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return OUTPUT_PATH_POJO + replaceDot(MyGeneratorConfig.PojoPackagePath,null) + "/" + tableInfo.getEntityName()+"Bean" + StringPool.DOT_JAVA;
            }
        });
        // C#实体类文件输出
//        list.add(new FileOutConfig(C_SHARP_ENTITY_TEMPLATE) {
//            @Override
//            public String outputFile(TableInfo tableInfo) {
//
//                return OUTPUT_PATH + "/java/" + replaceDot(PARENT_PACKAGE,C_SHARP_ENTITY_PACKAGE) +"/"+ tableInfo.getEntityName() + ".cs";
//            }
//        });

        // mapper xml文件输出
        list.add(new FileOutConfig(MAPPER_XML_TEMPLATE) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return OUTPUT_PATH + replaceDot(PARENT_PACKAGE, MAPPER_PACKAGE) + "/xml/" + tableInfo.getXmlName() + StringPool.DOT_XML;
            }
        });
        // mapper文件输出
        list.add(new FileOutConfig(MAPPER_TEMPLATE) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return OUTPUT_PATH + replaceDot(PARENT_PACKAGE, MAPPER_PACKAGE) + "/" + tableInfo.getMapperName() + StringPool.DOT_JAVA;
            }
        });
        // service文件输出
        list.add(new FileOutConfig(SERVICE_TEMPLATE) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return OUTPUT_PATH + replaceDot(PARENT_PACKAGE, SERVICE_PACKAGE) + "/" + tableInfo.getServiceName() + StringPool.DOT_JAVA;
            }
        });
        // service impl文件输出
        list.add(new FileOutConfig(SERVICE_IMPL_TEMPLATE) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return OUTPUT_PATH + replaceDot(PARENT_PACKAGE, SERVICE_IMPL_PACKAGE) + "/" + tableInfo.getServiceImplName() + StringPool.DOT_JAVA;
            }
        });
        // controller文件输出
        list.add(new FileOutConfig(CONTROLLER_TEMPLATE) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return OUTPUT_PATH + replaceDot(PARENT_PACKAGE, CONTROLLER_PACKAGE) + "/" + tableInfo.getControllerName() + StringPool.DOT_JAVA;
            }
        });

        // List.html文件输出
        list.add(new FileOutConfig(VUE_LIST) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                String firstLowerEntityName = StringUtils.firstToLowerCase(tableInfo.getEntityName());
                return MyGeneratorConfig.VuePath + firstLowerEntityName + "List.vue";
            }
        });

        // Add.html文件输出
        list.add(new FileOutConfig(VUE_LIST_JS) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return MyGeneratorConfig.JsPath + StringUtils.firstToLowerCase(tableInfo.getEntityName()) + ".js";
            }
        });

        // edit.html文件输出
        list.add(new FileOutConfig(VUE_LIST_EDIT) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return MyGeneratorConfig.VuePath + StringUtils.firstToLowerCase(tableInfo.getEntityName()) + "Edit.vue";
            }
        });
        return list;
    }

    /**
     * 判断文件是否存在
     *
     * @param path 路径
     * @return
     */
    private static boolean isExists(String path) {
        File file = new File(path);
        return file.exists();
    }

    /**
     * 将.替换成为文件分隔符
     */
    private static String replaceDot(String parent, String value) {
        if(value!=null){
            return (parent + "." + value).replaceAll("\\.", "/");
        }else{
            return parent.replaceAll("\\.", "/");
        }
    }

    /**
     * 自动创建
     */
    private static void createFile(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            File folder = file.getParentFile();
            if (!folder.exists()) {
                folder.mkdirs();
            }
            file = new File(filePath);
        }
    }

    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + "：");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "！");
    }
}